home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue56 / Splat / recordmain.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-02-25  |  13.7 KB  |  469 lines

  1. unit RecordMain;
  2.  
  3. // Splat.
  4. // Record a wave form for use in the Splat program.
  5. // Copyright ⌐ 2000 Tempest Software, Inc.
  6.  
  7. interface
  8.  
  9. uses
  10.   Windows, Messages, MMSystem, SysUtils, Classes, Graphics, Controls, Menus,
  11.   Forms, Dialogs, ComCtrls, StdCtrls, ActnList, ImgList, WideLabel;
  12.  
  13. type
  14.   EMciError = class(Exception)
  15.   private
  16.     fErrorCode: LongWord;
  17.   public
  18.     constructor Create(ErrorCode: LongWord); overload;
  19.     constructor Create(ErrorCode: LongWord; const Msg: string); overload;
  20.     constructor Create(ErrorCode: LongWord; const Fmt: string; const Args: array of const); overload;
  21.     property ErrorCode: LongWord read fErrorCode;
  22.   end;
  23.   TSortColumn = (scName, scSize, scDate);
  24.   TForm1 = class(TForm)
  25.     Label1: TLabel;
  26.     WaveList: TListView;
  27.     StatusBar: TStatusBar;
  28.     PopupMenu: TPopupMenu;
  29.     ActionList: TActionList;
  30.     PlayAction: TAction;
  31.     DeleteAction: TAction;
  32.     Play1: TMenuItem;
  33.     Delete1: TMenuItem;
  34.     ImageList: TImageList;
  35.     ChDirAction: TAction;
  36.     ChangeDirectory1: TMenuItem;
  37.     N1: TMenuItem;
  38.     ViewList1: TMenuItem;
  39.     ViewDetails1: TMenuItem;
  40.     Label2: TLabel;
  41.     KeyboardList: TComboBox;
  42.     Recording: TGroupBox;
  43.     WideLabel1: TWideLabel;
  44.     procedure FormKeyUp(Sender: TObject; var Key: Word;
  45.       Shift: TShiftState);
  46.     procedure FormKeyDown(Sender: TObject; var Key: Word;
  47.       Shift: TShiftState);
  48.     procedure ActionListUpdate(Action: TBasicAction; var Handled: Boolean);
  49.     procedure DeleteActionExecute(Sender: TObject);
  50.     procedure PlayActionExecute(Sender: TObject);
  51.     procedure FormCreate(Sender: TObject);
  52.     procedure ChDirActionExecute(Sender: TObject);
  53.     procedure ViewStyleExecute(Sender: TObject);
  54.     procedure WaveListColumnClick(Sender: TObject; Column: TListColumn);
  55.     procedure WaveListCompare(Sender: TObject; Item1, Item2: TListItem;
  56.       Data: Integer; var Compare: Integer);
  57.     procedure WaveListDeletion(Sender: TObject; Item: TListItem);
  58.     procedure KeyboardListChange(Sender: TObject);
  59.   private
  60.     { Private declarations }
  61.     RecordKey: Word;
  62.     SortColumn: TSortColumn;
  63.     SortAscending: Boolean;
  64.     function AddWaveFile(const FileName: string; Size: Integer = 0;
  65.       Date: TDateTime = 0): TListItem;
  66.     procedure GetKeyboardLayouts;
  67.     procedure GetWaveFiles;
  68.     procedure SetMode(const Mode: string);
  69.     procedure SetStatusInfo(const Info: WideString);
  70.     procedure SetKeyDisplay(const Value: WideString);
  71.   public
  72.     { Public declarations }
  73.   end;
  74.  
  75. var
  76.   Form1: TForm1;
  77.  
  78. implementation
  79.  
  80. uses CommCtrl, FileCtrl, KeyText, ZWave;
  81.  
  82. {$R *.DFM}
  83.  
  84. resourcestring
  85.   sRecording = 'Recording';
  86.   sRecorded = 'Recorded %s';
  87.   sCompressing = 'Compressing...';
  88.   sKilo = 'KB';
  89.  
  90. const
  91.   KiloBytes = 1024;
  92.  
  93. // Check a return code from an MCI function. Raise an exception for any error.
  94. procedure MciCheck(ErrorCode: LongWord); overload;
  95. begin
  96.   if ErrorCode <> 0 then
  97.     raise EMciError.Create(ErrorCode);
  98. end;
  99.  
  100. // Check a return code from an MCI function. Raise an exception for any error.
  101. // Use Msg as the exception message.
  102. procedure MciCheck(ErrorCode: LongWord; const Msg: string); overload;
  103. begin
  104.   if ErrorCode <> 0 then
  105.     raise EMciError.Create(ErrorCode, Msg);
  106. end;
  107.  
  108. // Check a return code from an MCI function. Raise an exception for any error.
  109. // Format an exception message from Fmt and Args.
  110. procedure MciCheck(ErrorCode: LongWord; const Fmt: string; const Args: array of const); overload;
  111. begin
  112.   if ErrorCode <> 0 then
  113.     raise EMciError.Create(ErrorCode, Fmt, Args);
  114. end;
  115.  
  116. { EMciError }
  117. // Exception class for MCI errors.
  118. constructor EMciError.Create(ErrorCode: LongWord);
  119. begin
  120.   Create(ErrorCode, '');
  121. end;
  122.  
  123. constructor EMciError.Create(ErrorCode: LongWord; const Msg: string);
  124. var
  125.   Buffer: array[0..128] of Char;
  126. begin
  127.   fErrorCode := ErrorCode;
  128.   MciGetErrorString(ErrorCode, Buffer, SizeOf(Buffer));
  129.   inherited Create(Msg + Buffer);
  130. end;
  131.  
  132. constructor EMciError.Create(ErrorCode: LongWord; const Fmt: string;
  133.   const Args: array of const);
  134. begin
  135.   Create(ErrorCode, Format(Fmt, Args));
  136. end;
  137.  
  138.  
  139. { TForm1 }
  140.  
  141. type
  142.   // Keep basic information about each file as the associated data
  143.   // in the list view.
  144.   PFileInfo = ^TFileInfo;
  145.   TFileInfo = record
  146.     Size: Integer;
  147.     Date: TDateTime;
  148.   end;
  149.  
  150. // Add a file to the list view.
  151. function TForm1.AddWaveFile(const FileName: string; Size: Integer; Date: TDateTime): TListItem;
  152. var
  153.   Search: TSearchRec;
  154.   Info: PFileInfo;
  155. begin
  156.   Result := WaveList.Items.Add;
  157.   Result.Caption := FileName;
  158.   if (Size = 0) or (Date = 0) then
  159.   begin
  160.     if FindFirst(FileName, 0, Search) = 0 then
  161.     begin
  162.       Size := Search.Size;
  163.       Date := FileDateToDateTime(Search.Time);
  164.       FindClose(Search);
  165.     end
  166.   end;
  167.   New(Info);
  168.   Info.Size := Size;
  169.   Info.Date := Date;
  170.   Result.Data := Info;
  171.   Result.SubItems.Add(IntToStr(Size div KiloBytes) + sKilo);
  172.   Result.SubItems.Add(DateTimeToStr(Date));
  173. end;
  174.  
  175. // Start recording a wave file for the key that the user has
  176. // pressed. Record only the first key pressed until the user
  177. // releases that key.
  178. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  179.   Shift: TShiftState);
  180. resourcestring
  181.   sCannotOpen = 'Cannot open ZWAV recorder:'#13#10;
  182.   sCannotRecord = 'Cannot record ZWAV file:'#13#10;
  183. var
  184.   DisplayText: WideString;
  185. begin
  186.   if RecordKey = 0 then
  187.   begin
  188.     // Not already recording a sound, so start recording.
  189.     MciCheck(mciSendString('open new type waveaudio alias wave', nil, 0, 0), sCannotOpen);
  190.     MciCheck(mciSendString('record wave', nil, 0, 0), sCannotRecord);
  191.  
  192.     // Remember which key is being recorded, and update the status bar.
  193.     RecordKey := Key;
  194.     SetMode(sRecording);
  195.     DisplayText := KeyCodeToDisplay(Key);
  196.     SetStatusInfo(DisplayText);
  197.     SetKeyDisplay(DisplayText);
  198.   end;
  199. end;
  200.  
  201. // Stop recording when the user releases the key. Make sure
  202. // the user is releasing the key that is being recorded (in case
  203. // the user presses multiple keys).
  204. procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word;
  205.   Shift: TShiftState);
  206. resourcestring
  207.   sCannotStop = 'Cannot stop recording ZWAV file:'#13#10;
  208.   sCannotSave = 'Cannot save ZWAV file (%s):'#13#10;
  209.   sCannotClose = 'Cannot close ZWAV recorder:'#13#10;
  210. var
  211.   FileName, ZFileName: string;
  212.   Item: TListItem;
  213. begin
  214.   if Key = RecordKey then
  215.   begin
  216.     MciCheck(mciSendString('stop wave', nil, 0, 0), sCannotStop);
  217.  
  218.     // Save the waveform to a file.
  219.     FileName := KeyCodeToText(RecordKey) + '.wav';
  220.     MciCheck(mciSendString(PChar('save wave ' + FileName), nil, 0, 0),
  221.              Format(sCannotSave, [FileName]));
  222.  
  223.     MciCheck(mciSendString('close wave', nil, 0, 0), sCannotClose);
  224.  
  225.     SetMode(sCompressing);
  226.     ZFileName := KeyCodeToText(RecordKey) + '.zwav';
  227.     Compress(FileName, ZFileName);
  228.     DeleteFile(FileName);
  229.     FileName := ZFileName;
  230.  
  231.     RecordKey := 0;
  232.     SetMode('');
  233.     SetKeyDisplay('');
  234.     SetStatusInfo(Format(sRecorded, [FileName]));
  235.  
  236.     // If the file is not already in the list, add it.
  237.     Item := WaveList.FindCaption(0, FileName, False, True, True);
  238.     if Item = nil then
  239.       Item := AddWaveFile(FileName);
  240.     Item.Selected := True;
  241.   end;
  242. end;
  243.  
  244. // Set the status mode in the left-hand panel of the status bar.
  245. procedure TForm1.SetMode(const Mode: string);
  246. begin
  247.   StatusBar.Panels[0].Text := Mode;
  248. end;
  249.  
  250. // Set the status information in the right-hand panel of the status bar.
  251. procedure TForm1.SetStatusInfo(const Info: WideString);
  252. begin
  253.   // Do the following: "StatusBar.Panels[1].Text := Info;"
  254.   // but using Unicode
  255.   SendMessageW(StatusBar.Handle, Sb_SetTextW, 1, LParam(PWideChar(Info)));
  256. end;
  257.  
  258. // Enable or disable actions according to the list view selection.
  259. // Only one file can be played at a time, so enable the Play action
  260. // only when exactly one file is selected.
  261. // Enable Delete when one or more files is selected.
  262. procedure TForm1.ActionListUpdate(Action: TBasicAction; var Handled: Boolean);
  263. begin
  264.   PlayAction.Enabled := WaveList.SelCount = 1;
  265.   DeleteAction.Enabled := WaveList.SelCount > 0;
  266. end;
  267.  
  268. // Delete the selected file or files. Confirm the deletion with the user first.
  269. // If one file is selected, show the file name in the prompt. Otherwise,
  270. // just show the number of files to be deleted.
  271. procedure TForm1.DeleteActionExecute(Sender: TObject);
  272. resourcestring
  273.   sConfirmOne = 'Are you sure you want to delete %s?';
  274.   sDeletedOne = '%s deleted';
  275.   sConfirmMany = 'Are you sure you want to delete the selected files?';
  276.   sDeletedMany = '%d files deleted';
  277. var
  278.   FileName: string;
  279.   Count, I: Integer;
  280. begin
  281.   Assert(WaveList.Selected <> nil);
  282.   if WaveList.SelCount = 1 then
  283.   begin
  284.     FileName := WaveList.Selected.Caption;
  285.     if mrYes = MessageDlg(Format(sConfirmOne, [FileName]), mtConfirmation, [mbYes, mbNo], 0) then
  286.     begin
  287.       WaveList.Selected.Delete;
  288.       DeleteFile(FileName);
  289.       SetStatusInfo(Format(sDeletedOne, [FileName]));
  290.     end;
  291.   end
  292.   else if mrYes = MessageDlg(sConfirmMany, mtConfirmation, [mbYes, mbNo], 0) then
  293.   begin
  294.     Count := 0;
  295.     for I := WaveList.Items.Count-1 downto 0 do
  296.     begin
  297.       if WaveList.Items[I].Selected then
  298.       begin
  299.         FileName := WaveList.Items[I].Caption;
  300.         WaveList.Items[I].Delete;
  301.         if DeleteFile(FileName) then
  302.           Inc(Count);
  303.       end;
  304.     end;
  305.     SetStatusInfo(Format(sDeletedMany, [Count]));
  306.   end;
  307. end;
  308.  
  309. // Play the selected file.
  310. procedure TForm1.PlayActionExecute(Sender: TObject);
  311. begin
  312.   if WaveList.Selected <> nil then
  313.     Win32Check(PlayCompressedSound(PChar(WaveList.Selected.Caption), 0, Snd_FileName or Snd_NoDefault or Snd_Async));
  314.   SetStatusInfo('');
  315. end;
  316.  
  317. // Load all the keyboard layouts that the user has installed.
  318. // The user can select a new keyboard layout at runtime.
  319. procedure TForm1.GetKeyboardLayouts;
  320. var
  321.   I: Integer;
  322.   Index: Integer;
  323.   Handle: HKL;
  324. begin
  325.   for I := 0 to Languages.Count-1 do
  326.   begin
  327.     Handle := LoadKeyboardLayout(PChar(IntToHex(Languages.LocaleID[I], 8)), Klf_Substitute_OK or Klf_NoTellShell);
  328.     if Handle <> 0 then
  329.     begin
  330.       Index := KeyboardList.Items.AddObject(Languages.Name[I], TObject(Handle));
  331.       // Pre-select the current keyboard layout.
  332.       if Handle = GetKeyboardLayout(0) then
  333.         KeyboardList.ItemIndex := Index;
  334.     end;
  335.   end;
  336. end;
  337.  
  338. // Start the program by fetching all the .ZWAV files in the current directory.
  339. procedure TForm1.FormCreate(Sender: TObject);
  340. begin
  341.   GetWaveFiles;
  342.   GetKeyboardLayouts;
  343. end;
  344.  
  345. // Get all the .ZWAV files in the current directory and show them
  346. // in the list view.
  347. procedure TForm1.GetWaveFiles;
  348. resourcestring
  349.   sCaption = 'Record Sounds - ';
  350. var
  351.   Search: TSearchRec;
  352. begin
  353.   WaveList.Items.BeginUpdate;
  354.   try
  355.     // Add the directory name to the form and application captions.
  356.     Caption := sCaption + GetCurrentDir;
  357.     Application.Title := Caption;
  358.     WaveList.Items.Clear;
  359.     if FindFirst('*.zwav', faAnyFile, Search) = 0 then
  360.       try
  361.         repeat
  362.           if (Search.Attr and faDirectory) = 0 then
  363.             AddWaveFile(Search.Name, Search.Size, FileDateToDateTime(Search.Time));
  364.         until FindNext(Search) <> 0;
  365.       finally
  366.         FindClose(Search);
  367.       end;
  368.   finally
  369.     WaveList.Items.EndUpdate;
  370.   end;
  371. end;
  372.  
  373. // Change directories and get the .ZWAV files in the new directory.
  374. procedure TForm1.ChDirActionExecute(Sender: TObject);
  375. resourcestring
  376.   sDlgCaption = 'Select folder for ZWAV files';
  377. var
  378.   Dir: string;
  379. begin
  380.   if SelectDirectory(sDlgCaption, '', Dir) then
  381.   begin
  382.     if not SysUtils.SetCurrentDir(Dir) then
  383.       RaiseLastWin32Error;
  384.     GetWaveFiles;
  385.   end;
  386. end;
  387.  
  388. // Change the list view style.
  389. procedure TForm1.ViewStyleExecute(Sender: TObject);
  390. begin
  391.   WaveList.ViewStyle := TViewStyle((Sender as TComponent).Tag);
  392.   (Sender as TMenuItem).Checked := True;
  393. end;
  394.  
  395. // Change the sort order of the list view.
  396. procedure TForm1.WaveListColumnClick(Sender: TObject; Column: TListColumn);
  397. begin
  398.   if SortColumn = TSortColumn(Column.Tag) then
  399.     SortAscending := not SortAscending
  400.   else
  401.   begin
  402.     SortColumn := TSortColumn(Column.Tag);
  403.     SortAscending := True;
  404.   end;
  405.   WaveList.AlphaSort;
  406. end;
  407.  
  408. procedure TForm1.WaveListCompare(Sender: TObject; Item1, Item2: TListItem;
  409.   Data: Integer; var Compare: Integer);
  410. resourcestring
  411.   sCannotHappen = 'WaveListCompare: internal error, SortColumn=%d';
  412. var
  413.   Info1, Info2: PFileInfo;
  414. begin
  415.   Info1 := Item1.Data;
  416.   Info2 := Item2.Data;
  417.   Assert(Info1 <> nil);
  418.   Assert(Info2 <> nil);
  419.   case SortColumn of
  420.   scName:
  421.     Compare := AnsiCompareFileName(Item1.Caption, Item2.Caption);
  422.   scSize:
  423.     Compare := Info1.Size - Info2.Size;
  424.   scDate:
  425.     if Info1.Date > Info2.Date then
  426.       Compare := 1
  427.     else if Info1.Date < Info2.Date then
  428.       Compare := -1
  429.     else
  430.       Compare := 0;
  431.   else
  432.     raise Exception.CreateFmt(sCannotHappen, [Ord(SortColumn)]);
  433.   end;
  434.  
  435.   if not SortAscending then
  436.     Compare := -Compare;
  437. end;
  438.  
  439. // When a list view item is removed from the list view,
  440. // delete the associated data record.
  441. procedure TForm1.WaveListDeletion(Sender: TObject; Item: TListItem);
  442. begin
  443.   FreeMem(Item.Data);
  444. end;
  445.  
  446. // When the user selects a new keyboard, tell Windows
  447. // to activate that keyboard layout.
  448. procedure TForm1.KeyboardListChange(Sender: TObject);
  449. var
  450.   Handle: HKL;
  451. begin
  452.   if KeyboardList.ItemIndex >= 0 then
  453.   begin
  454.     Handle := HKL(KeyboardList.Items.Objects[KeyboardList.ItemIndex]);
  455.     Win32Check(ActivateKeyboardLayout(Handle, 0) <> 0);
  456.   end;
  457. end;
  458.  
  459. const
  460.   Margin = 8;
  461.  
  462. // Set the text for the key display.
  463. procedure TForm1.SetKeyDisplay(const Value: WideString);
  464. begin
  465.   WideLabel1.Caption := Value;
  466. end;
  467.  
  468. end.
  469.